package com.yedean.mall.token;

import org.springframework.data.redis.core.RedisTemplate;

import java.sql.Date;
import java.time.Instant;

/**
 * TokenRedisClient
 *
 * @author yedean
 * @date 2022/8/17
 */
public class TokenRedisClient implements TokenClient<UserInfo> {

    public static final int RETRY_COUNT = 3;

    private final TokenGenerator tokenGenerator;
    private final long accessTokenExpireSeconds;

    private final long refreshTokenExpireSeconds;

    private final TokenRedisAccessor accessor;


    public TokenRedisClient(String namespace, RedisTemplate<String, Object> redisTemplate, TokenGenerator generator, long accessTokenExpireSeconds, long refreshTokenExpireSeconds) {
        this.tokenGenerator = generator;
        this.accessTokenExpireSeconds = accessTokenExpireSeconds;
        this.refreshTokenExpireSeconds = refreshTokenExpireSeconds;
        this.accessor = new TokenRedisAccessor(redisTemplate, namespace);
    }

    @Override
    public TokenGroup askToken(UserInfo userInfo) {
        //生成refresToken并插入
        RefreshTokenEntity refreshTokenEntity = generateRefreshToken(userInfo);
        // 生成access token并插入
        AccessTokenEntity accessToken = generateAccessToken(refreshTokenEntity.getRefreshToken(), userInfo);

        // 更新refresh token中的access token
        refreshTokenEntity.setAccessToken(accessToken.getAccessToken());
        accessor.updateRefreshToken(refreshTokenEntity);

        // 返回token新
        return new TokenGroup(accessToken, refreshTokenEntity);
    }

    @Override
    public TokenGroup refreshToken(String refreshToken) {
        // 获取refresh token信息
        RefreshTokenEntity refreshTokenEntity = accessor.selectRefreshToken(refreshToken);
        if (refreshTokenEntity == null) {
            return null;
        }

        // 移除access token
        accessor.deleteAccessToken(refreshTokenEntity.getAccessToken());

        // 生成并插入新的access token
        AccessTokenEntity accessTokenEntity = generateAccessToken(refreshToken, refreshTokenEntity.getUserInfo());

        // 更新refresh token绑定的access token
        refreshTokenEntity.setAccessToken(accessTokenEntity.getAccessToken());
        accessor.updateRefreshToken(refreshTokenEntity);

        return new TokenGroup(accessTokenEntity, refreshTokenEntity);
    }

    @Override
    public UserInfo exchangeUserInfo(String accessToken) {
        AccessTokenEntity entity = accessor.selectAccessToken(accessToken);
        if (entity == null) {
            return null;
        }
        return entity.getUserInfo();
    }

    @Override
    public void disableTokenGroupByAccessToken(String accessToken) {
        AccessTokenEntity accessTokenEntity = accessor.selectAccessToken(accessToken);
        if (accessTokenEntity == null) {
            return;
        }
        accessor.deleteAccessToken(accessToken);
        accessor.deleteRefreshToken(accessTokenEntity.getRefreshToken());
    }


    private RefreshTokenEntity generateRefreshToken(UserInfo userInfo) {
        RefreshTokenEntity entity;
        Instant refreshExpired = Instant.now().plusSeconds(refreshTokenExpireSeconds);

        for (int count = 0; count < RETRY_COUNT; count++) {
            String token = tokenGenerator.generator();
            entity = new RefreshTokenEntity(token, "", userInfo, Date.from(refreshExpired));
            boolean success = accessor.insertRefreshToken(entity);
            if (success) {
                return entity;
            }
        }

        throw new RuntimeException("refresh token插入失败！");
    }


    private AccessTokenEntity generateAccessToken(String refreshToken, UserInfo userInfo) {
        AccessTokenEntity entity;
        Instant accessExpired = Instant.now().plusSeconds(accessTokenExpireSeconds);

        for (int count = 0; count < RETRY_COUNT; count++) {
            String token = tokenGenerator.generator();
            entity = new AccessTokenEntity(token, refreshToken, userInfo, Date.from(accessExpired));
            boolean success = accessor.insertAccessToken(entity);
            if (success) {
                return entity;
            }
        }

        throw new RuntimeException("access token插入失败！");
    }


}
